#include "NBFPredictor.hpp"
#include "GraphFunctions.hpp"


using namespace TopologicalLearner;
using namespace std;

//----------------------------------------------------------------
EditOpDist NBFPredictor::Predict(const Graph& G) const
{
    cout << "Decomposing Graph" << endl;
    vector< std::pair<Graph, VertexMappings > > C = DecomposeGraph(G);

    vector< Graph> kTmpVec;
    for(int i=0; i < C.size(); i++)
        kTmpVec.push_back(C[i].first);

    //GraphFunctions::SaveVectorOfGraphs(*m_pD, "decompose/", kTmpVec,100);

    EditOpDist kBestDist;
    double dBest = -1.0;
    unsigned int iBestIndex = 0;

    bool bUnique = m_pD->IsUnique();

    for(unsigned int i=0; i < C.size(); i++)
    {
        //-----------
        //GraphFunctions::GetDistribution(vector<Graph>(), *m_pD, )



        EditOpDist kDist = GraphFunctions::GetDistribution(C, *m_pD, i, C[i].first, m_iExcludeID, bUnique);


       // cout << "kDist size: " << kDist.size() << endl;

        EditOpDist::iterator it;
        for(it = kDist.begin(); it != kDist.end(); it++ )
            if( it->second > dBest )
            {
                kBestDist = kDist;
                dBest = it->second;
                iBestIndex = i;
            }
    }

   // cout << "C:" << C.size() << endl;
   // cout << "Mapping [0] size: " << C[0].second.size() << endl;
   // cout << "Size dist:" << kBestDist.size() << endl;

    // Now transform the edit operation map from the local to global
    if( !bUnique )
    {
        EditOpDist kResDist;
        EditOpDist::iterator it;
        for(it = kBestDist.begin(); it != kBestDist.end(); it++ )
        {
            EditOp kOld = it->first;
            /*
            cout << "Old edit op: " << endl;
            GraphFunctions::PrintEditOp(G, kOld, *m_pD);
                cout << "New edit op:" << endl;*/

            // Go through all the possible mappings of this graph
            for( unsigned int i=0; i < C[iBestIndex].second.size(); i++ )
            {
                Vertex v1 = (C[iBestIndex].second[i])[(Vertex)kOld.get<1>()];
                Vertex v2;
                // If its an edge
                if( !kOld.get<0>() )
                    v2 = (C[iBestIndex].second[i])[(Vertex)kOld.get<2>()];
                else // If its a vertex addition, semantic category stays the same
                    v2 = kOld.get<2>();
                if( (!kOld.get<0>() && !edge(v1, v2, G).second ) || kOld.get<0>() )
                {
                    EditOp kNew = boost::make_tuple( kOld.get<0>(), v1, v2 );
                    //GraphFunctions::PrintEditOp(G, kNew, *m_pD);
                    kResDist[kNew] = it->second;
                }
            }

        }
        return kResDist;
    }
    else
        return kBestDist;
}
//----------------------------------------------------------------
vector< std::pair<Graph, VertexMappings > > NBFPredictor::DecomposeGraph(const Graph& G) const
{
    cout << "Getting FSG's included in input graph" << endl;
    std::set< std::pair< const FrequentSubgraph*, VertexMappings > >
     pPatterns = m_pD->GetOccuringFrequentSubgraphs(G,2);

    vector< std::pair< const FrequentSubgraph*, VertexMappings > > kSortedList;

    copy (pPatterns.begin(), pPatterns.end(),         // source range
          back_inserter(kSortedList));              // destination range



    vector< std::pair<Graph, VertexMappings > > C;

    // If no frequent subgraphs are found, add the whole graph
    if( pPatterns.size() == 0 )
    {
        VertexMappings tmp; // TODO: Fix this and really create an identity mapping
        //std::vector< std::map<Vertex, Vertex> > VertexMappings;
        map<Vertex, Vertex> kTmpMap;
        Iter_t vi, vi_end;
        // For each vertex
        for (tie(vi, vi_end) = vertices(G); vi != vi_end; ++vi)
            kTmpMap[*vi] = *vi;
        tmp.push_back(kTmpMap);
        C.push_back( make_pair(G, tmp) );
        return C;
    }

    // 2. Choose the frequent subgraph of the highest frequency

   // sort( kSortedList.begin(), kSortedList.end(), CPFrequentSubgraph_Sorter() );

    //---------------------
    for(unsigned int i=0; i < kSortedList.size(); i++)
    {
        //C.push_back( kSortedList[i]->G );
        C.push_back( make_pair(kSortedList[i].first->G, kSortedList[i].second) );
    }
    return C;

    //---------------------
/*
    // Since it is sorted according to ascending order, the highest is at the end.
    C.push_back( pPatterns.back().first->kMeta );
    pPatterns.pop_back();
    //--------------------------------------------------

    Graph kUnion;
    while(num_vertices(kUnion) < num_vertices(G))
    {
        kUnion = GraphUnion(C);
        bool bFound;
        bFound = false;

        for(int i=0; i < C.size(); i++)
        {
            pair<bool, Graph*> Gres;

            // Check if we can find another frequent subgraph to connect to G_i
            // The criterion is that they share atleast one vertex and that adding it would make the union graph
            // "bigger"
            if( (Gres = FindCommonFreqSubgraph(pPatterns, C[i], kUnion, C)).first )
            {
                C.push_back(*Gres.second);
                bFound = true;
                break;
            }
        }
        if(!bFound)
        {

            //-----------------------------------------
            // Did not find any
            //Graph kHyp = GraphUnion(Gi);

            if( num_vertices(G) > num_vertices(kUnion) )
            {

            Graph kDiff = GetDifferenceGraph(G, kUnion);
            //SaveGraph("diffg.dot", kDiff);
            // Now get the components of kDiff
            std::vector<int> component(num_vertices(kDiff));
            int num = connected_components(kDiff, &component[0]);

            // For each of these components, we will add them to GiTmp
            // First we need to form the induced subgraphs of them, since they are just
            // assignement of each vertex to a component number
            for(int i=0; i < num; i++)
            {
                vector<Vertex> Fi;
                for(int j=0; j != component.size(); ++j)
                    if( component[j] == i )
                        Fi.push_back(j);

                // Fi is a list of vertices for the i:th component
                C.push_back(InducedSubgraph(Fi, kDiff));
            }

            }
            //-----------------------------------------
            return C;
        }
    }
*/
    return C;

}
//----------------------------------------------------------------
